/*
 * Copyright (C) 2010 Zaizi Limited.
 * 
 * This file is part of Zaizi's Alfresco FISE integration
 * 
 * Alfresco FISE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco FISE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco FISE. If not, see <http://www.gnu.org/licenses/>.
 */
package org.zaizi.fiseiks.alfresco.service;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.ws.rs.core.MediaType;

import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;

import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;

/**
 * Implement {@link FiseIksSearchService}
 * 
 * @author efoncubierta
 * @see FiseIksAbstractService
 */
public class FiseIksSearchServiceImpl extends FiseIksAbstractService implements FiseIksSearchService
{
    private Log logger = LogFactory.getLog(FiseIksSearchServiceImpl.class);
    
    // RDF tags
    private static final String PROP_ENTITYLABEL = "http://fise.iks-project.eu/ontology/entity-label";
    private static final String PROP_ENTITYREFERENCE = "http://fise.iks-project.eu/ontology/entity-reference";
    private static final String PROP_ENTITYTYPE = "http://fise.iks-project.eu/ontology/entity-type";
    
    /**
     * Initialization method
     */
    public void init()
    {
    	// Do nothing...
    }
    
    /*
     * (non-Javadoc)
     * @see org.zaizi.fiseiks.alfresco.service.FiseIksSearchService#get(org.alfresco.service.cmr.repository.NodeRef)
     */
	public SemanticContent get(NodeRef nodeRef)
	{
		// Content FISE urls
		final String storeMetadataUrl = FISE_STORE_METADATA + "/" + nodeRef.getId();
		final String storeContentUrl  = FISE_STORE_CONTENT  + "/" + nodeRef.getId();
		
		final SemanticContent content = new SemanticContent();
		content.setNodeRef(nodeRef);
		content.setMetadataUrl(storeMetadataUrl);
		content.setContentUrl(storeContentUrl);
		
		// Call FISE
        ClientResponse response = getRestClient().get(storeMetadataUrl, new MediaType("application", "rdf+json"));
        
        // Check HTTP status code
        int status = response.getStatus();
        if(status != 200 && status != 201 && status != 202)
        {
        	logger.error("Error retrieving content metadata " + nodeRef + " from Fise at " + storeMetadataUrl);
        }
        else
        {
        	logger.debug("Successfully loaded metadata " + nodeRef + " from " + storeContentUrl);
        	
        	try
        	{
        		// Iterate over the entries
				JSONObject res = new JSONObject(response.getEntity(String.class));
				Iterator<?> iter = res.keys();
				while(iter.hasNext())
				{
					String k = (String)iter.next();
					JSONObject obj = res.getJSONObject(k);
					
					// Skip entries that don't contains any of these
					if(!obj.has(PROP_ENTITYLABEL) || !obj.has(PROP_ENTITYREFERENCE) || !obj.has(PROP_ENTITYTYPE))
					{
						continue;
					}
					
					String label = obj.getJSONArray(PROP_ENTITYLABEL).getJSONObject(0).getString("value");
					String reference = obj.getJSONArray(PROP_ENTITYREFERENCE).getJSONObject(0).getString("value");
					
					// Iterate over entity types
					JSONArray typesJson = obj.getJSONArray(PROP_ENTITYTYPE);
					for(Integer i=0; i<typesJson.length(); i++)
					{
						JSONObject typeJson = typesJson.getJSONObject(i);
						content.addEntity(typeJson.getString("value"), label, reference);
					}
				}
			}
        	catch (ClientHandlerException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        	catch (UniformInterfaceException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        	catch (JSONException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        }
		
		return content;
	}

	/*
	 * (non-Javadoc)
	 * @see org.zaizi.fiseiks.alfresco.service.FiseIksSearchService#search(java.lang.String, java.lang.String)
	 */
	public List<NodeRef> search(String site, String entity)
	{
		List<NodeRef> nodes = new ArrayList<NodeRef>();
		
		// Build the SPARQL query
		final StringBuilder sb = new StringBuilder();
		sb.append("PREFIX fise: <http://fise.iks-project.eu/ontology/>").append("\n");
		sb.append("SELECT distinct ?content").append("\n");
		sb.append("WHERE {").append("\n");
		sb.append("  ?x fise:entity-label \"" + entity + "\"^^<http://www.w3.org/2001/XMLSchema#string> .").append("\n");
		sb.append("  ?x fise:extracted-from ?content");
		sb.append("}").append("\n");
		// sb.append("ORDER BY DESC(?extraction_time) LIMIT " + DEFAULT_MAX_RESULTS);
		
		String url = "/sparql?query=";
		try
		{
			url += URLEncoder.encode(sb.toString(), "UTF-8");
		}
		catch (UnsupportedEncodingException e)
		{
			// TODO: Handle error
			logger.error(e.getMessage(), e);
		}
		
		ClientResponse response = getRestClient().get(url, new MediaType("application", "sparql-results+xml"));
		
		// Check HTTP status code
        int status = response.getStatus();
        if(status != 200 && status != 201 && status != 202)
        {
        	logger.error("Error searching from Fise at " + url);
        }
        else
        {
        	logger.debug("Successfully search from Fise from " + url);
        	
        	try
        	{
        		// Iterate over results
				JSONObject res = XML.toJSONObject(response.getEntity(String.class));

				JSONArray results;
				// Exists "result" ...
				if(!res.getJSONObject("sparql").getJSONObject("results").has("result"))
				{
					results = new JSONArray();
				}
				// ... "result" is array ...
				else if(res.getJSONObject("sparql").getJSONObject("results").optJSONArray("result") == null)
				{
					results = new JSONArray();
					results.put(res.getJSONObject("sparql").getJSONObject("results").getJSONObject("result"));
				}
				else
				{
					results = res.getJSONObject("sparql").getJSONObject("results").getJSONArray("result");
				}
				
				for(Integer i=0; i<results.length(); i++)
				{
					final JSONObject result = results.getJSONObject(i);
					final String uri = result.getJSONObject("binding").getString("uri");
					final String[] uriParts = uri.split("/");
					final String nodeId = uriParts[uriParts.length - 1];
					
					final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/" + nodeId);
					nodes.add(nodeRef);
				}
				
			}
        	catch (ClientHandlerException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        	catch (UniformInterfaceException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        	catch (JSONException e)
        	{
				// TODO: Handle error
        		logger.error(e.getMessage(), e);
			}
        }
        
		return nodes;
	}
}
